package middleware

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

// ResponseWriter 包装器，用于捕获状态码、响应大小和内容
type ResponseWriter struct {
	http.ResponseWriter
	statusCode int
	size       int
	body       bytes.Buffer
}

func (rw *ResponseWriter) WriteHeader(code int) {
	rw.statusCode = code
	rw.ResponseWriter.WriteHeader(code)
}

func (rw *ResponseWriter) Write(b []byte) (int, error) {
	size, err := rw.ResponseWriter.Write(b)
	rw.size += size
	// 保存响应内容（仅保存前1KB避免内存问题）
	if rw.body.Len() < 1024 {
		rw.body.Write(b)
	}
	return size, err
}

// Hijack 实现http.Hijacker接口，支持WebSocket
func (rw *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hijacker, ok := rw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, fmt.Errorf("ResponseWriter does not implement http.Hijacker")
	}
	return hijacker.Hijack()
}

// LogLevel 日志级别
type LogLevel int

const (
	LogLevelError LogLevel = iota
	LogLevelWarn
	LogLevelInfo
	LogLevelDebug
)

// Logger 日志记录器
type Logger struct {
	fileLogger    *log.Logger
	consoleLogger *log.Logger
	logFile       *os.File
	level         LogLevel
}

var apiLogger *Logger

// InitLogger 初始化日志记录器
func InitLogger() error {
	return InitLoggerWithLevel("info")
}

// InitLoggerWithLevel 使用指定级别初始化日志记录器
func InitLoggerWithLevel(levelStr string) error {
	// 解析日志级别
	var level LogLevel
	switch strings.ToLower(levelStr) {
	case "error":
		level = LogLevelError
	case "warn", "warning":
		level = LogLevelWarn
	case "info":
		level = LogLevelInfo
	case "debug":
		level = LogLevelDebug
	default:
		level = LogLevelInfo
	}

	// 获取可执行文件所在目录
	execDir, err := getExecutableDir()
	if err != nil {
		// 如果获取失败，使用当前目录作为后备方案
		execDir = "."
	}

	// 创建logs目录在可执行文件同目录下
	logsDir := filepath.Join(execDir, "logs")
	if err := os.MkdirAll(logsDir, 0755); err != nil {
		return fmt.Errorf("failed to create log directory: %w", err)
	}

	// 创建日志文件名（按日期）
	logFileName := fmt.Sprintf("api_%s.log", time.Now().Format("2006-01-02"))
	logFilePath := filepath.Join(logsDir, logFileName)

	// 打开日志文件
	logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		return fmt.Errorf("failed to open log file: %w", err)
	}

	// 创建多写器（同时写入控制台和文件）
	multiWriter := io.MultiWriter(os.Stdout, logFile)
	
	apiLogger = &Logger{
		fileLogger:    log.New(logFile, "", 0),
		consoleLogger: log.New(multiWriter, "", 0),
		logFile:       logFile,
		level:         level,
	}

	return nil
}

// getExecutableDir 获取可执行文件所在目录
func getExecutableDir() (string, error) {
	exe, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(exe), nil
}

// CloseLogger 关闭日志文件
func CloseLogger() {
	if apiLogger != nil && apiLogger.logFile != nil {
		apiLogger.logFile.Close()
	}
}

// LoggingMiddleware API请求日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 记录开始时间
		start := time.Now()

		// 包装ResponseWriter
		wrapped := &ResponseWriter{
			ResponseWriter: w,
			statusCode:     200, // 默认状态码
		}

		// 获取客户端IP
		clientIP := getClientIP(r)

		// 读取请求体（如果有）
		var requestBody string
		if r.Body != nil && (r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH") {
			bodyBytes, _ := io.ReadAll(r.Body)
			r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 重新设置body供后续使用
			
			// 限制请求体长度显示（最多500字符）
			if len(bodyBytes) > 500 {
				requestBody = string(bodyBytes[:500]) + "..."
			} else {
				requestBody = string(bodyBytes)
			}
			
			// 如果是JSON，去掉换行符和多余空格
			requestBody = strings.ReplaceAll(requestBody, "\n", "")
			requestBody = strings.ReplaceAll(requestBody, "\t", "")
		}

		// 执行下一个处理器
		next.ServeHTTP(wrapped, r)

		// 计算处理时间
		duration := time.Since(start)

		// 根据路径判断是否为API请求
		if strings.HasPrefix(r.URL.Path, "/api/") {
			// API请求 - 记录详细信息
			logDetailedAPIRequest(r, wrapped, clientIP, start, duration, requestBody)
		} else {
			// 静态文件请求 - 简单记录
			logStaticRequest(r, wrapped, clientIP, start, duration)
		}

		// 如果是错误状态码，额外记录
		if wrapped.statusCode >= 400 {
			logErrorRequest(r, wrapped, clientIP, start)
		}
	})
}

// logDetailedAPIRequest 记录详细的API请求信息
func logDetailedAPIRequest(r *http.Request, wrapped *ResponseWriter, clientIP string, start time.Time, duration time.Duration, requestBody string) {
	if apiLogger == nil {
		return
	}

	// 基本信息
	basicLog := fmt.Sprintf("API [%s] %s %s %s %d %dB %v",
		start.Format("2006-01-02 15:04:05"),
		clientIP,
		r.Method,
		r.RequestURI,
		wrapped.statusCode,
		wrapped.size,
		duration,
	)

	// 控制台显示基本信息
	apiLogger.consoleLogger.Printf(basicLog)

	// 根据日志级别决定是否记录详细信息
	if apiLogger.level >= LogLevelDebug {
		// DEBUG级别：记录超详细信息
		detailLog := fmt.Sprintf("API [%s] %s %s %s %d %dB %v\n  ├─ Headers: %s\n  ├─ Query: %s\n  ├─ Body: %s\n  ├─ UserAgent: %s\n  └─ Response: %s",
			start.Format("2006-01-02 15:04:05"),
			clientIP,
			r.Method,
			r.RequestURI,
			wrapped.statusCode,
			wrapped.size,
			duration,
			formatHeaders(r.Header),
			formatQueryParams(r.URL.Query()),
			requestBody,
			r.UserAgent(),
			formatResponse(wrapped.body.String()),
		)
		apiLogger.fileLogger.Printf(detailLog)
	} else if apiLogger.level >= LogLevelInfo {
		// INFO级别：记录中等详细信息
		infoLog := fmt.Sprintf("API [%s] %s %s %s %d %dB %v\n  ├─ Query: %s\n  └─ Body: %s",
			start.Format("2006-01-02 15:04:05"),
			clientIP,
			r.Method,
			r.RequestURI,
			wrapped.statusCode,
			wrapped.size,
			duration,
			formatQueryParams(r.URL.Query()),
			requestBody,
		)
		apiLogger.fileLogger.Printf(infoLog)
	} else {
		// WARN/ERROR级别：只记录基本信息
		apiLogger.fileLogger.Printf(basicLog)
	}
}

// logStaticRequest 记录静态文件请求
func logStaticRequest(r *http.Request, wrapped *ResponseWriter, clientIP string, start time.Time, duration time.Duration) {
	if apiLogger == nil {
		return
	}

	logEntry := fmt.Sprintf("STATIC [%s] %s %s %s %d %dB %v",
		start.Format("2006-01-02 15:04:05"),
		clientIP,
		r.Method,
		r.RequestURI,
		wrapped.statusCode,
		wrapped.size,
		duration,
	)

	apiLogger.fileLogger.Printf(logEntry)
}

// logErrorRequest 记录错误请求
func logErrorRequest(r *http.Request, wrapped *ResponseWriter, clientIP string, start time.Time) {
	if apiLogger == nil {
		return
	}

	errorLog := fmt.Sprintf("ERROR [%s] %s %s %s %d - %s",
		start.Format("2006-01-02 15:04:05"),
		clientIP,
		r.Method,
		r.RequestURI,
		wrapped.statusCode,
		http.StatusText(wrapped.statusCode),
	)

	apiLogger.consoleLogger.Printf(errorLog)
}

// formatHeaders 格式化请求头
func formatHeaders(headers http.Header) string {
	if len(headers) == 0 {
		return "none"
	}

	var parts []string
	// 排序头部名称
	var keys []string
	for k := range headers {
		// 过滤敏感信息
		if strings.ToLower(k) != "authorization" && strings.ToLower(k) != "cookie" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	for _, k := range keys {
		v := headers[k]
		if len(v) > 0 {
			parts = append(parts, fmt.Sprintf("%s=%s", k, v[0]))
		}
	}

	result := strings.Join(parts, ", ")
	if len(result) > 200 {
		return result[:200] + "..."
	}
	return result
}

// formatQueryParams 格式化查询参数
func formatQueryParams(params url.Values) string {
	if len(params) == 0 {
		return "none"
	}

	var parts []string
	for k, v := range params {
		if len(v) > 0 {
			parts = append(parts, fmt.Sprintf("%s=%s", k, v[0]))
		}
	}

	result := strings.Join(parts, "&")
	if len(result) > 200 {
		return result[:200] + "..."
	}
	return result
}

// formatResponse 格式化响应内容
func formatResponse(response string) string {
	if response == "" {
		return "empty"
	}

	// 去除换行符和多余空格
	response = strings.ReplaceAll(response, "\n", "")
	response = strings.ReplaceAll(response, "\t", " ")
	
	if len(response) > 200 {
		return response[:200] + "..."
	}
	return response
}

// getClientIP 获取客户端真实IP
func getClientIP(r *http.Request) string {
	// 尝试从X-Forwarded-For头获取
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
		ips := strings.Split(xff, ",")
		if len(ips) > 0 {
			return strings.TrimSpace(ips[0])
		}
	}

	// 尝试从X-Real-IP头获取
	if xri := r.Header.Get("X-Real-IP"); xri != "" {
		return strings.TrimSpace(xri)
	}

	// 使用RemoteAddr
	ip := r.RemoteAddr
	if colon := strings.LastIndex(ip, ":"); colon != -1 {
		ip = ip[:colon]
	}
	
	// 移除IPv6的方括号
	ip = strings.Trim(ip, "[]")
	
	return ip
} 